Android Interview - 2

思维导图,用于面试前复习Android的基础知识点,用自己的话总结出面试要回答到的重点,可能包括一些不包括该知识点的全面概括与解析,其中也包括了面试中被问到的知识。

image

Activity

  • Activity的四种启动模式

    • standard 标准,默认,先进后出
    • singleTop (栈顶复用模式)当再次启动的界面位于栈顶,复用该实例,当再次启动的界面不位于栈顶,重新创建实例
    • singleTask (栈内复用模式)具有clearTop的功能
    • singleInstance (栈内唯一实例),系统没有该实例,新建一个任务栈,并将放入栈顶;系统有实例,使用该实例并转到前台
  • Activity的启动流程

    • Activity的管理核心是AMS(ActivityManagerService),是一个独立的进程,ActivtyThread是每一个应用程序所在进程的主线程,循环的处理消息。他们之间通过binder来进行进程间的通信。
    • 流程
      1. AMS发现这个activity还没启动,就会通知zygot孵化出应用进程
      2. 然后在dalvik应用进程里执行ActivityThread的main方法
      3. 应用进程会通知AMS已经启动,AMS保存了一个应用进程的代理对象,这样AMS可以通过代理对象控制应用进程,AMS然后通知创建入口Activity实例,并执行他的生命周期方法
  • Activity的通信方式

    • startActivityForResult
    • EventBus
    • LocalBroadcastReceiver

Service

  • 两种启动方式

    • bindservice: 绑定服务,调用者挂了,服务也会挂掉。绑定者可以调用服务里面的方法。
    • startservice:开启者挂了,服务还在后台长期的运行。
      开启者不能调用服务里面的方法。
  • IntentService : 会创建独立的worker线程来处理OnHandlerIntent()方法实现的代码,无需处理多线程的问题所有请求处理完成后,IntentService会自动停止。

  • 与Activity的通信方式

    • Callback加接口
    • 用Service发广播,activity接收广播
    • 可以通过bindService的方式,先在Activity里实现一个ServiceConnection接口,并将该接口传递给bindService()方法,在ServiceConnection接口的onServiceConnected()方法里执行相关操作。

BroadcastReciver

  • 接收原理
    • 通过Binder机制向ActivityMangerService注册广播
    • 通过Binder机制向ActivityMangerService发送广播
    • AMS查找符合相应条件的广播,将广播发送到BroadcastReceiver所在的消息队列中。
    • BroadcastReceiver所在消息队列拿到此广播后,回调它的onReceive()方法
  • 广播的分类与注册方式
    • 分类:分为有序广播或者无序广播,有序广播的广播接收者按照Priority属性值从大-小排序接收。
    • 注册:动态注册是在代码中注册,跟随组件的生命周期,组件结束前需要动态移除,防止内存溢出。静态注册即使app退出还是可以接收,占内存,耗电大。

ContentProvider

  • ContentProvider中的数据监控机制与Android系统中的广播机制的区别
    • 前者通过URI来把通知的发送者和接收者关联在一起,后者是通过Intent
    • 前者通知注册中心是由ContentService服务来扮演,后者是AMS服务来扮演
    • 前者接收数据更新通知要继承ContentObserver,后者继承BroadcastReceiver

Fragment

  • 通信方式
    • Fragment调用Activity时,直接使用getActivity()拿到Activity实例
    • Actvity调用Fragment时,采用接口回调的形式
    • Fragment调用Fragment时,用FindFragmentById()

Handler机制

  • 角色

    • Message:消息,线程间通讯的数据单元。
    • MessageQueue:消息队列,主要用来向消息队列添加消息和取出消息,先进先出。
    • Looper:消息循环器,主要用来把消息分发给相应的处理者。
    • Handler:消息处理器,主要向消息队列发送各种消息以及处理各种消息。
  • 流程

    1. Handler通过sendMessage()发送消息Message到消息队列MessageQueue。
    2. Looper通过loop()不断提取消息,并且将Messeage交给目标handler来进行处理
    3. 调用自身的回调方法handleMessage()来处理Message

事件分发

  • 事件分发的三个核心方法:dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()。
  • 事件分发的顺序:Activity(Window) -> ViewGroup -> View
  • 事件分发核心伪代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //父View调用方法入口进行事件分发
    public boolean dispatchTouchEvent(MotionEvent motionEvent){
    boolean consume = false;
    //是否进行拦截
    if(onInterceptTouchEvent(event)){
    consume = onTouchEvent(event);
    }else{
    consume = child.dispatchTouchEvent(event);
    }
    return consume;
    }

View的绘制

  • 绘制流程
    • onMeasure():测量视图,从顶层父view到子view递归调用measure()方法,完成绘制
    • onLayout():确定视图的位置,从顶层父View到子View递归调用layout()方法,父View将上一步measure()方法得到的子View的布局大小和布局参数,将子View放在合适的位置上
    • onDraw():绘制最终的视图,首先ViewRoot创建一个Canvas对象,然后调用onDraw()方法进行绘制。onDraw()方法的绘制流程为:① 绘制视图背景。② 绘制画布的图层。 ③ 绘制View内容。④ 绘制子视图,如果有的话。⑤ 还原图层。⑥ 绘制滚动条。

APK打包流程与安装流程

  • 打包流程

    主要是资源打包和代码打包

    1. 通过aapt工具将资源文件(包括AndroidManifest.xml、布局文件、各种xml资源等)打包成R.java文件
    2. 处理AIDL文件,生成对应的Java文件
    3. Javac工具编译项目源码,生成class文件
    4. DX工具将class文件转换成dex文件,主要完成java字节码转换成Dalvik字节码
    5. 通过ApkBuilder工具将资源文件、DEX文件打包生成APK文件
    6. 利用keystore生成签名文件
    7. 正式版本apk还会使用ZipAlign工具进行对齐处理,可以加快内存映射访问APK文件
      的速度。
  • 安装流程

    复制APK到/data/app目录下

    1. 资源管理器解析APK里的资源文件
    2. 解析AndroidManifest文件,并在data/data目录下创建对应的应用数据目录
    3. 然后对dex文件进行优化,保存在dalvik-cache目录下
    4. 解析AndroidManifest文件,创建出四大组件,注册到PackageManagerService中
    5. 安装完成后发送安装完成的广播(AppReceiver-PACKAGE_ADDED)。

Binder

  • Android Binder是用来做进程通信的,Android的各个应用以及系统服务都运行在独立的进程中,它们的通信都依赖于Binder。

  • Linux进程通讯的方式有哪些

    • 管道:在创建时分配一个page大小的内存,缓存区大小比较有限
    • 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
    • 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
    • Socket:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
    • 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    • 信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
  • Binder的好处

    • 高性能:从数据拷贝次数来看Binder只需要进行一次内存拷贝,而管道、消息队列、Socket都需要两次,共享内存不需要拷贝,Binder的性能仅次于共享内存。
    • 稳定:Binder基于C/S架构,客户端与服务端彼此独立,稳定性较好。
    • 安全性:Android为每个应用分配了UID,用来作为鉴别进程的重要标志,Android内部也依赖这个UID进行权限管理。标记完全是在用户空间控制的,没有放在内核空间,因此有被恶意篡改的可能,因此Binder的安全性更高。

常见的内存泄露

  • 持有静态的Context(Activity)引用
  • 持有静态的View引用
  • 不正确的单例模式,比如单例持有Activity或者Context
  • bitmap,io等资源的回收
  • 内部类&匿名内部类实例无法释放,而内部类又持有外部类的强引用,导致外部类无法释放

MVC,MVP,MVVM对比分析

  • MVC:Activity/Fragment即是View也是,Controller,项目越大,耦合越大。
  • MVP:为了达到View和Controller进行解耦,引入了Presenter进行解耦,将视图逻辑和业务逻辑进行分离
  • MVVM:使用ViewModel代替Presenter,实现数据与View的双向绑定,可以使用data-bindging将数据绑定进xml中

热修复的原理

  • 利用PathClassLoader和DexClassLoader去加载与bug类同名的类,替换掉bug类,进而达到修复bug的目的,原理是在app打包的时候阻止类打上CLASS_ISPREVERIFIED的标志,然后在热修复的时候动态修改BaseDexClassLoader对象间接引用的dexElements,替换掉旧的类.

Parcelable和Serializable区别

  • 序列化的两种方式,序列化就是将对象变成二进制流,便于存储和传输。
    • Serializable是java实现的一套序列化方式,可能会触发频繁的IO操作,效率比较低,适合将对象存储到磁盘上的情况。
    • Parcelable是Android提供一套序列化机制,它将序列化后的字节流写入到一个共享内存中,其他对象可以从这块共享内存中读出字节流,并反序列化成对象。因此效率比较高,适合在对象间或者进程间传递信息。

线程池

  • Android中常见的4种线程池
    • FixedThreadPool: 线程数量固定的线程池,它只有核心线程
    • CachedThreadPool: 线程数量不固定的线程池,它只有非核心线程
    • ScheduledThreadPool: 核心线程数量固定,非核心线程数量没有限制的线程池,主要用于执行定时任务和具有固定周期的任务
    • SingleThreadPool: 只有一个核心线程的线程池,确保了所有的任务都在同一个线程中按顺序执行。

进程保活

  • 提升进程的优先级,降低进程被杀死的概率,监控手机锁屏事件,在屏幕锁屏时启动一个像素的Activity,在用户解锁时将Activity销毁掉,前台Activity可以将进程变成前台进程,优先级升级到最高。
  • 拉活已经被杀死的进程,利用广播拉活Activity。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器